"""
1. 创建工厂方法
类方法常用于提供备用的构造方法，通常称为工厂方法。当你需要根据不同的输入或数据源创建类的实例时，类方法非常有用。例如，当你需要从字符串、文件或数据库等外部数据源创建对象时，可以使用类方法
"""
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    def from_string(cls, info_str):
        name, age = info_str.split(", ")
        return cls(name, int(age))  # 返回一个 Person 的实例

# 使用类方法从字符串创建对象
person1 = Person.from_string("Alice, 30")
print(person1.name)  # 输出 Alice
print(person1.age)   # 输出 30

"""
2. 修改类级别的状态
类方法可以用于修改类的属性，而不是实例的属性。当你需要跨多个实例共享某些状态时，类方法非常有用。例如，计数器、日志记录、配置管理等场景。
"""
class MyClass:
    count = 0  # 类属性

    def __init__(self):
        MyClass.count += 1

    @classmethod
    def get_count(cls):
        return cls.count

# 创建实例
obj1 = MyClass()
obj2 = MyClass()

# 通过类方法访问类属性
print(MyClass.get_count())  # 输出 2，表示已经创建了两个实例
"""
3. 实现单例模式
在某些场景下，你可能只希望某个类有一个实例，类方法可以用来确保这一点，通常用于实现单例模式。通过类方法控制实例化逻辑，可以确保一个类只有一个实例。
"""
class Singleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

    @classmethod
    def get_instance(cls):
        return cls._instance

# 获取单例实例
obj1 = Singleton()
obj2 = Singleton()

print(obj1 is obj2)  # 输出 True，确保只有一个实例

"""
4. 继承和类的多态性
当类有继承关系时，类方法也可以用于让子类调用父类的类方法，尤其是在工厂方法和状态管理中。当子类需要使用父类的类方法时，它可以继承父类并调用父类的方法，保持多态性。
"""
class Animal:
    species = "Unknown"

    @classmethod
    def info(cls):
        return f"This is an {cls.species}"

class Dog(Animal):
    species = "Dog"

class Cat(Animal):
    species = "Cat"

print(Dog.info())  # 输出 "This is a Dog"
print(Cat.info())  # 输出 "This is a Cat"

"""
5. 访问和修改类的配置或状态
在一些情况下，你需要在多个实例中共享某些配置信息或状态。类方法可以用来访问和修改这些信息。
"""

class Config:
    _config = {}

    @classmethod
    def set(cls, key, value):
        cls._config[key] = value

    @classmethod
    def get(cls, key):
        return cls._config.get(key, None)

# 设置配置
Config.set('database', 'mysql')
Config.set('host', 'localhost')

# 获取配置
print(Config.get('database'))  # 输出 'mysql'
print(Config.get('host'))      # 输出 'localhost'
